#include <string.h>
#include <stdio.h>
#include <memory.h>
#include <stdlib.h>
#include "sm_trace.h"
#include "xmsg.h"
#include "itc.h"
#include "app_proxy.h"
#include "common.h"


#define RM_SESSION_COUNT 	128
typedef struct
{
	char 			m_isUsed;
	unsigned char 	m_tcid;	
	unsigned short 	m_ssnb;
}rm_session_table_t;

static rm_session_table_t s_rm_tab_session[RM_SESSION_COUNT];

int rm_init(void)
{
	int i;

	SM_TRACE_ENTRY(TRMOD_APP, "rm_init() - entry\r\n");

	for(i=0;i<RM_SESSION_COUNT;i++)
		s_rm_tab_session[i].m_isUsed=0;

	SM_TRACE_RETURN(TRMOD_APP, "rm_init() - return 0\r\n");
	return 0;
}


static void on_apdu_profile_enquiry(unsigned short ssnb)
{
	int				i;
	int				validRessCount = 0;
	unsigned char* 	ptr;
	unsigned char* 	pbBuf = NULL;

	SM_TRACE_ENTRY(TRMOD_APP, "on_apdu_profile_enquiry() - entry,ssnb=%d\r\n", ssnb);

	validRessCount = 0;
	for(i=0, validRessCount=0;i<RESOURCE_COUNT;i++)
	{
		if(g_tab_ress[i].m_ressId)
			validRessCount++;
	}
	
	pbBuf=(unsigned char *)malloc(validRessCount*4);
	if(NULL==pbBuf)
	{
		SM_TRACE_ERROR(TRMOD_APP, "on_apdu_profile_enquiry() - memory out\r\n");
		return;
	}

	ptr = pbBuf;
	for(i=0;i<RESOURCE_COUNT;i++)
	{
		if(!g_tab_ress[i].m_ressId)
			continue;
		asnWriteInt32(ptr, g_tab_ress[i].m_ressId);
		ptr += 4;
	}

	SM_TRACE_INFO(TRMOD_APP, "on_apdu_profile_enquiry() - send profile_reply\r\n");
	send_app_apdu_to_proxy(ssnb, CI_APDU_TAG_PROFILE, pbBuf, validRessCount*4); //profile reply
	
	if (pbBuf)
	{
		free(pbBuf);
		pbBuf = NULL;
	}

	SM_TRACE_RETURN(TRMOD_APP, "on_apdu_profile_enquiry() - return\r\n");
}


static int on_apdu_profile_changed(short ssnb)
{
	SM_TRACE_ENTRY(TRMOD_APP, "on_apdu_profile_changed() - entry,ssnb=%d\r\n", ssnb);

	SM_TRACE_INFO(TRMOD_APP, "on_apdu_profile_enquiry() - send profile_enq\r\n");
	send_app_apdu_to_proxy(ssnb, CI_APDU_TAG_PROFILE_ENQ, NULL, 0); //profile enq

	SM_TRACE_RETURN(TRMOD_APP, "on_apdu_profile_changed() - return 0\r\n");
	return 0;
}


static void on_apdu_profile_reply(short ssnb, unsigned char* pbBuf, int length)
{
	int				i;
	int				tcid = 0;
	int				isRessChanged = 0;
	unsigned char*	ptr = NULL;
	unsigned long	ressId;

	SM_TRACE_ENTRY(TRMOD_APP, "on_apdu_profile_reply() - entry,ssnb=%d,pbBuf=0x%08X,"
				"length=%d\r\n", ssnb, pbBuf, length);

	if(NULL==pbBuf)
	{
		SM_TRACE_ERROR(TRMOD_APP, "on_apdu_profile_reply() - invalid parameter\r\n");
		return;
	}

	if(length%4)
	{
		SM_TRACE_ERROR(TRMOD_APP, "on_apdu_profile_reply() - invalid length=%d\r\n", length);
		return;
	}

	for(i=0;i<RM_SESSION_COUNT;i++)
	{
		if(s_rm_tab_session[i].m_ssnb==ssnb)
		{
			tcid=s_rm_tab_session[i].m_tcid;
			break;
		}
	}
	if(i>=RM_SESSION_COUNT)
	{
		SM_TRACE_ERROR(TRMOD_APP, "on_apdu_profile_reply() - not found ssnb=%d\r\n", ssnb);
		return;
	}

	isRessChanged = 0;//indicated whether resource table has changed because of this profile_reply.
	ptr = pbBuf;
	for (; length > 0; )
	{
		ressId = asnReadInt32(ptr);
		ptr += 4;
		length -= 4;

		if (PRIVATE_RESOURCE_ID_TYPE==(ressId&RESOURCE_ID_TYPE_MASK)) //private resource
		{
			for (i = 0; i < RESOURCE_COUNT; i++)
			{
				if(!g_tab_ress[i].m_ressId) //empty entry
				{
					g_tab_ress[i].m_isLocalRess=0;
					g_tab_ress[i].m_ressId=ressId;
					g_tab_ress[i].m_tcid=tcid;
					isRessChanged=1; // indicate that profile changed
					break;
				}
			}
			if (i >= RESOURCE_COUNT)
			{
				SM_TRACE_ERROR(TRMOD_APP,"on_apdu_profile_reply() - resource table full\r\n");
				return;
			}
		}
		else //public resource
		{
			if((RM_RESOURCE_ID&RESOURCE_CLASS_MASK)==(ressId&RESOURCE_CLASS_MASK))
				continue;//RM can't be replaced
			if((AI_RESOURCE_ID&RESOURCE_CLASS_MASK)==(ressId&RESOURCE_CLASS_MASK))
				continue;//ai can't be replaced
			if((CAS_RESOURCE_ID&RESOURCE_CLASS_MASK)==(ressId&RESOURCE_CLASS_MASK))
				continue;//ca can't be replaced
			//if((hc_resource_id&RESOURCE_CLASS_MASK)==(ressId&RESOURCE_CLASS_MASK))
			//	continue;//hc can't be replaced
			if((MMI_RESOURCE_ID&RESOURCE_CLASS_MASK)==(ressId&RESOURCE_CLASS_MASK))
				continue;//mmi can't be replaced
			if((DT_RESOURCE_ID&RESOURCE_CLASS_MASK)==(ressId&RESOURCE_CLASS_MASK))
				continue;//dt can't be replaced

			// now check resouces that have already existed to see which resource can
			// be substituted by this new resource.
			for (i = 0; i < RESOURCE_COUNT; i++)
			{
				if(g_tab_ress[i].m_ressId)
				{
					if(((g_tab_ress[i].m_ressId&RESOURCE_CLASS_MASK)==(ressId&RESOURCE_CLASS_MASK))
						&&((g_tab_ress[i].m_ressId&RESOURCE_TYPE_MASK)==(ressId&RESOURCE_TYPE_MASK))
						&&((g_tab_ress[i].m_ressId&RESOURCE_VERSION_MASK)<(ressId&RESOURCE_VERSION_MASK)))
					{
						//substitude the resource.
						//among the resource provided by host, only the low-speed communications may be
						//replaced by a module provided one. if the host has no low-speed communication
						//resource, nothing to be worried about.
						g_tab_ress[i].m_ressId=ressId;
						g_tab_ress[i].m_tcid=tcid;
						g_tab_ress[i].m_isLocalRess=0;
						isRessChanged=1; //indicate that profile changed
						break;
					}
				}
			}

			if (i >= RESOURCE_COUNT)
			{
				//this is a new resource, and no same class resource existing in the resource table.
				//the resource can be inserted to the g_tab_ress
				for (i = 0; i < RESOURCE_COUNT; i++)
				{
					if(!g_tab_ress[i].m_ressId) //empty entry
					{
						g_tab_ress[i].m_ressId=ressId;
						g_tab_ress[i].m_isLocalRess=0;
						g_tab_ress[i].m_tcid=tcid;
						isRessChanged=1;
						break;
					}
				}
				if (i >= RESOURCE_COUNT)
				{
					SM_TRACE_ERROR(TRMOD_APP,"on_apdu_profile_reply() - ress table full\r\n");
					break;
				}
			}
		} //end public resource
	}

	if(isRessChanged) //resource table changed.
	{
		for(i=0;i<RM_SESSION_COUNT;i++)
		{	
			//notify all the other sessions of the change of resource table
			if(s_rm_tab_session[i].m_isUsed)
			{
				SM_TRACE_INFO(TRMOD_APP, "on_apdu_profile_enquiry() - send profile_changed\r\n");
				send_app_apdu_to_proxy(s_rm_tab_session[i].m_ssnb, CI_APDU_TAG_PROFILE_CHANGE, NULL, 0); //profile changed
			}
		}
	}
	else //resource table not changed.
	{
		SM_TRACE_INFO(TRMOD_APP, "on_apdu_profile_enquiry() - send profile_changed\r\n");
		send_app_apdu_to_proxy(s_rm_tab_session[i].m_ssnb, CI_APDU_TAG_PROFILE_CHANGE, NULL, 0); //profile changed
	}

	SM_TRACE_RETURN(TRMOD_APP, "on_apdu_profile_reply() - return 0\r\n");
}


int rm_on_message(xmsg_t* pXmsg)
{
	int					i;
	unsigned short		ssnb = 0;
	unsigned char		tcid = 0;
	int					isRessChanged = 0;
	xmsg_t				xmsg;
	
	SM_ASSERT(pXmsg);	
	
	SM_TRACE_ENTRY(TRMOD_APP, "rm_on_message() - entry,pXmsg=0x%08X\r\n", pXmsg);

	SM_TRACE_INFO(TRMOD_APP, "rm_on_message() - %s(0x%02X) received\r\n",
				XMSG_NAME(pXmsg->m_type),pXmsg->m_type);
	
	switch(pXmsg->m_type)
	{
	case APP_OPEN_SS_REQ:
		ssnb = pXmsg->m_bd.m_app_open_ss_req.m_ssnb;
		tcid = pXmsg->m_bd.m_app_open_ss_req.m_tcid;
		SM_TRACE_INFO(TRMOD_APP, "rm_on_message() - APP_OPEN_SS_REQ received, ssnb=%d\r\n", ssnb);
		for(i=0;i<RM_SESSION_COUNT;i++)
		{
			if(!s_rm_tab_session[i].m_isUsed)
				break;
		}
		memset(&xmsg, 0x00, sizeof(xmsg));
		xmsg.m_sndrcver = (RM << 4) | RM;
		xmsg.m_type  = APP_OPEN_SS_RSP;
		xmsg.m_bd.m_app_open_ss_rsp.m_tcid = tcid;
		xmsg.m_bd.m_app_open_ss_rsp.m_ssnb = ssnb;
		xmsg.m_bd.m_app_open_ss_rsp.m_ressId = pXmsg->m_bd.m_app_open_ss_req.m_ressId;		
		if(i<RM_SESSION_COUNT)
		{
			xmsg.m_bd.m_app_open_ss_rsp.m_status = SS_OK;
			s_rm_tab_session[i].m_isUsed=1;
			s_rm_tab_session[i].m_ssnb=ssnb;
			s_rm_tab_session[i].m_tcid=tcid;
		}
		else
		{
			xmsg.m_bd.m_app_open_ss_rsp.m_status = SS_NOTAVAILABLE;
		}
		SM_TRACE_INFO(TRMOD_APP, "rm_on_message() - will send APP_OPEN_SS_RSP to PROXY\r\n");
		ITC_SendXmsg(&xmsg);
		break;
	
	case APP_SS_OPENED:
		SM_TRACE_INFO(TRMOD_APP, "rm_on_message() - APP_SS_OPENED received\r\n");
		ssnb=pXmsg->m_bd.m_app_ss_opened.m_ssnb;
			SM_TRACE_INFO(TRMOD_APP,"rm_on_message() - send profile enquiry, ssnb: %d\r\n", ssnb);
			send_app_apdu_to_proxy(ssnb, CI_APDU_TAG_PROFILE_ENQ, NULL, 0);
			
		break;

	case APP_SS_BROKEN:
		ssnb=pXmsg->m_bd.m_app_ss_broken.m_ssnb;
		for(i=0;i<RM_SESSION_COUNT;i++)
		{
			if((s_rm_tab_session[i].m_isUsed)
				&&(s_rm_tab_session[i].m_ssnb==ssnb))
			{
				s_rm_tab_session[i].m_isUsed=0; //unvalid the current item
			}
		}
		break;

	case APP_APDU:
	{
		unsigned long 	apduTag;
		unsigned long 	bodyLen;
		long 			totalLen;
		unsigned char* 	pbBody = NULL;
		unsigned char* 	pbApdu = NULL;

		ssnb = pXmsg->m_bd.m_app_apdu.m_ssnb;
		totalLen = pXmsg->m_bd.m_app_apdu.m_apduLen;
		pbApdu = pXmsg->m_bd.m_app_apdu.m_pbApdu;
		for (; totalLen > 0; )
		{
			pbBody=decode_apdu(pbApdu, &apduTag, &bodyLen);
			totalLen = totalLen - (((unsigned long)pbBody)-((unsigned long)pbApdu)) - bodyLen;
			pbApdu = pbBody+bodyLen; //point to the next apdu
			
			if(totalLen<0)
			{
				SM_TRACE_ERROR(TRMOD_APP,"rm_on_message() - length error in apdu\r\n");
				break;
			}
			
			switch(apduTag)
			{
			case CI_APDU_TAG_PROFILE_ENQ://profile enquiry
				SM_TRACE_INFO(TRMOD_APP,"rm_on_message() - on_apdu_profile_enquiry\r\n");
				on_apdu_profile_enquiry((unsigned short)ssnb);
				break;
			case CI_APDU_TAG_PROFILE://profile reply
				SM_TRACE_INFO(TRMOD_APP,"rm_on_message() - on_apdu_profile_reply\r\n");
				on_apdu_profile_reply((unsigned short)ssnb, pbBody, bodyLen);
				break;
			case CI_APDU_TAG_PROFILE_CHANGE://profile changed
				on_apdu_profile_changed((unsigned short)ssnb);
				break;
			default:
				SM_TRACE_ERROR(TRMOD_APP,"rm_on_message() - unknown apdu apduTag=0x%06X\r\n", apduTag);
				break;
			}
		}
		break;
	}

	case APP_TC_BROKEN:
		tcid=pXmsg->m_bd.m_app_tc_broken.m_tcid;
		isRessChanged=0;
		for(i=0;i<RESOURCE_COUNT;i++)
		{
			if((g_tab_ress[i].m_ressId)&&(g_tab_ress[i].m_isLocalRess==0)
				&&(g_tab_ress[i].m_tcid==tcid))
			{
				g_tab_ress[i].m_ressId = 0;
				isRessChanged=1;//resource changed.
			}
		}
		if(isRessChanged)
		{
			//notify all the other sessions of the change of resource table
			for(i=0;i<RM_SESSION_COUNT;i++)
			{
				if(s_rm_tab_session[i].m_isUsed)
				{
					SM_TRACE_INFO(TRMOD_APP, "rm_on_message() - send profile_changed\r\n");
					send_app_apdu_to_proxy(s_rm_tab_session[i].m_ssnb, CI_APDU_TAG_PROFILE_CHANGE, NULL, 0); //profile changed
				}
			}
		}
		break;

	default:
		SM_TRACE_ERROR(TRMOD_APP,"rm_on_message() - unkown mess type=0x%02X\r\n", pXmsg->m_type);
		break;
	}


	SM_TRACE_RETURN(TRMOD_APP, "rm_on_message() - return 0\r\n");
	return 0;
}


